1 module hip.game2d.ninepatch;
2 public import hip.api.renderer.texture;
3 public import hip.game2d.sprite;
4 import hip.game2d.renderer_data;
5 
6 enum NinePatchType
7 {
8     SCALED,
9     TILED //I Think this effect is quite ugly, but maybe it'll be useful at some time
10 }
11 
12 class NinePatch
13 {
14     uint width, height;
15     float x, y;
16     float scaleX, scaleY;
17     protected HipSprite[9] sprites;
18     protected HipSpriteVertex[9*4] vertices;
19     IHipTexture texture;
20     NinePatchType stretchStrategy;
21 
22     this(uint width, uint height, IHipTexture tex, NinePatchType type = NinePatchType.SCALED)
23     {
24         this.width = width;
25         this.height = height;
26         x = y = 0;
27         scaleX = scaleY = 1;
28         texture = tex;
29         stretchStrategy = type;
30         for(int i = 0; i < 9; i++)
31             sprites[i] = new HipSprite(tex);
32 
33         setTextureRegions();
34         setSize(width, height);
35     }
36 
37     void setSize(int width, int height)
38     {
39         this.width = width;
40         this.height = height;
41         build();
42     }
43 
44     /**
45     *   The arguments will be divided by the texture width and height for
46     *   generating the UVs
47     */
48     void setTextureRegions(uint x, uint y, uint width, uint height)
49     {
50         int texWidth = sprites[0].getTextureWidth;
51         int texHeight = sprites[0].getTextureHeight;
52 
53         float tx = cast(float)x/texWidth;
54         float ty = cast(float)y/texHeight;
55 
56         float xw = (cast(float)width/3.0f)/texWidth;
57         float yh = (cast(float)height/3.0f)/texHeight;
58         float xw2 = xw*2;
59         float xw3 = xw*3;
60         float yh2 = yh*2;
61         float yh3 = yh*3;
62 
63         sprites[TOP_LEFT].setRegion (tx+0,   ty+0, tx+xw,  ty+yh);
64         sprites[TOP_MID].setRegion  (tx+xw,  ty+0, tx+xw2, ty+yh);
65         sprites[TOP_RIGHT].setRegion(tx+xw2, ty+0, tx+xw3, ty+yh);
66 
67         sprites[MID_LEFT].setRegion (tx+0,  ty+yh, tx+xw,  ty+yh2);
68         sprites[MID_MID].setRegion  (tx+xw, ty+ yh,tx+ xw2,ty+ yh2);
69         sprites[MID_RIGHT].setRegion(tx+xw2,ty+ yh,tx+ xw3,ty+ yh2);
70 
71         sprites[BOT_LEFT].setRegion (tx+0,  ty+yh2, tx+xw,  ty+yh3);
72         sprites[BOT_MID].setRegion  (tx+xw, ty+ yh2,tx+ xw2,ty+ yh3);
73         sprites[BOT_RIGHT].setRegion(tx+xw2,ty+ yh2,tx+ xw3,ty+ yh3);
74     }
75 
76     /**
77     *   Cuts the entire image in 9 slices
78     */
79     void setTextureRegions()
80     {
81         setTextureRegions(0, 0, sprites[0].getTextureWidth, sprites[0].getTextureHeight);
82     }
83 
84     void build()
85     {
86         float xScalingFactor = cast(float)(width - cast(float)(sprites[TOP_LEFT].width*2));
87         if(sprites[TOP_LEFT].width != 0)
88             xScalingFactor/= cast(float)sprites[TOP_LEFT].width;
89         else  
90             xScalingFactor = 0;
91 
92         float yScalingFactor = cast(float)height - cast(float)(sprites[TOP_LEFT].height*2);
93         if(sprites[TOP_LEFT].height != 0)
94             yScalingFactor/= cast(float)sprites[TOP_LEFT].height;
95         else
96             yScalingFactor = 0;
97 
98         if(xScalingFactor < 1) xScalingFactor = 1;
99         if(yScalingFactor < 1) yScalingFactor = 1;
100 
101 
102         int spWidth = sprites[TOP_LEFT].width;
103         int spHeight = sprites[TOP_LEFT].height;
104 
105         int px2 = width-spWidth;
106         if(px2 < spWidth) px2 = spWidth;
107 
108         int py2 = height-spHeight;
109         if(py2 < spHeight) py2 = spHeight;
110 
111 
112         //First, take care of those which don't scale.
113         sprites[TOP_LEFT].setPosition(x, y);
114         sprites[TOP_RIGHT].setPosition(x + px2, y);
115         sprites[BOT_LEFT].setPosition(x, y + py2);
116         sprites[BOT_RIGHT].setPosition(x + px2, y + py2);
117 
118         //Now, those which scales in only one direction
119         sprites[TOP_MID].setPosition(x+spWidth, y);
120         sprites[MID_LEFT].setPosition(x, y+spHeight);
121         sprites[MID_RIGHT].setPosition(x+ px2, y+spHeight);
122         sprites[BOT_MID].setPosition(x+spWidth, y + py2);
123         sprites[MID_MID].setPosition(spWidth+x, spHeight+y);
124 
125 
126         if(stretchStrategy == NinePatchType.SCALED)
127         {
128             sprites[TOP_MID].setScale(xScalingFactor, 1);
129             sprites[MID_LEFT].setScale(1, yScalingFactor);
130             sprites[MID_RIGHT].setScale(1, yScalingFactor);
131             sprites[BOT_MID].setScale(xScalingFactor, 1);
132 
133             //The last one
134             sprites[MID_MID].setScale(xScalingFactor,  yScalingFactor);
135         }
136         else
137         {
138             sprites[TOP_MID].setTiling(xScalingFactor, 1);
139             sprites[MID_LEFT].setTiling(1, yScalingFactor);
140             sprites[MID_RIGHT].setTiling(1, yScalingFactor);
141             sprites[BOT_MID].setTiling(xScalingFactor, 1);
142 
143             //The last one
144             sprites[MID_MID].setTiling(xScalingFactor,  yScalingFactor);
145         }
146 
147         // uint thresholdWidth = spWidth*2;
148         // uint thresholdHeight = spHeight*2;
149         
150         // if(width < thresholdWidth)
151         // {
152         //     float sX = (width/2.0)/thresholdWidth;
153         //     sprites[TOP_LEFT].setScale(sX, sprites[TOP_LEFT].scaleY);
154         //     sprites[TOP_RIGHT].setScale(sX, sprites[TOP_RIGHT].scaleY);
155 
156         //     sprites[MID_LEFT].setScale(sX, sprites[MID_LEFT].scaleY);
157         //     sprites[MID_RIGHT].setScale(sX, sprites[MID_RIGHT].scaleY);
158 
159         //     sprites[BOT_LEFT].setScale(sX, sprites[BOT_LEFT].scaleY);
160         //     sprites[BOT_RIGHT].setScale(sX, sprites[BOT_RIGHT].scaleY);
161 
162         //     sprites[TOP_MID].setScale(0,0);
163         //     sprites[MID_MID].setScale(0,0);
164         //     sprites[BOT_MID].setScale(0,0);
165         // }
166         // if(height < thresholdHeight)
167         // {
168         //     float sY = (height/2.0)/thresholdHeight;
169         //     sprites[TOP_LEFT].setScale(sprites[TOP_LEFT].scaleX, sY);
170         //     sprites[TOP_RIGHT].setScale(sprites[TOP_RIGHT].scaleX, sY);
171 
172         //     sprites[MID_LEFT].setScale(sprites[MID_LEFT].scaleX, sY);
173         //     sprites[MID_RIGHT].setScale(sprites[MID_RIGHT].scaleX, sY);
174 
175         //     sprites[BOT_LEFT].setScale(sprites[BOT_LEFT].scaleX, sY);
176         //     sprites[BOT_RIGHT].setScale(sprites[BOT_RIGHT].scaleX, sY);
177 
178         //     sprites[TOP_MID].setScale(0,0);
179         //     sprites[MID_MID].setScale(0,0);
180         //     sprites[BOT_MID].setScale(0,0);
181         // }
182 
183         uint i = 0;
184         vertices[i++..i*4] = sprites[TOP_LEFT].getVertices();
185         vertices[i++..i*4] = sprites[TOP_MID].getVertices();
186         vertices[i++..i*4] = sprites[TOP_RIGHT].getVertices();
187         vertices[i++..i*4] = sprites[MID_LEFT].getVertices();
188         vertices[i++..i*4] = sprites[MID_MID].getVertices();
189         vertices[i++..i*4] = sprites[MID_RIGHT].getVertices();
190         vertices[i++..i*4] = sprites[BOT_LEFT].getVertices();
191         vertices[i++..i*4] = sprites[BOT_MID].getVertices();
192         vertices[i++..i*4] = sprites[BOT_RIGHT].getVertices();
193     }
194 
195     void setTopLeft(uint u1, uint v1, uint u2, uint v2){sprites[TOP_LEFT].setRegion(u1,v1,u2,v2);}
196     void setTopMid(uint u1, uint v1, uint u2, uint v2){sprites[TOP_MID].setRegion(u1,v1,u2,v2);}
197     void setTopRight(uint u1, uint v1, uint u2, uint v2){sprites[TOP_RIGHT].setRegion(u1,v1,u2,v2);}
198     
199 
200     void setMidLeft (uint u1, uint v1, uint u2, uint v2){sprites[MID_LEFT].setRegion(u1,v1,u2,v2);}
201     void setMidMid  (uint u1, uint v1, uint u2, uint v2){sprites[MID_MID].setRegion(u1,v1,u2,v2);}
202     void setMidRight(uint u1, uint v1, uint u2, uint v2){sprites[MID_RIGHT].setRegion(u1,v1,u2,v2);}
203 
204     void setBotLeft (uint u1, uint v1, uint u2, uint v2){sprites[BOT_LEFT].setRegion(u1,v1,u2,v2);}
205     void setBotMid  (uint u1, uint v1, uint u2, uint v2){sprites[BOT_MID].setRegion(u1,v1,u2,v2);}
206     void setBotRight(uint u1, uint v1, uint u2, uint v2){sprites[BOT_RIGHT].setRegion(u1,v1,u2,v2);}
207 
208 
209     void setPosition(float x, float y)
210     {
211         this.x = x;
212         this.y = y;
213         updatePosition();
214     }
215 
216 
217    void setColor(HipColor color)
218    {
219        int quad = 0;
220        for(int i = 0; i < 9; i++)
221        {
222             quad = cast(int)(i*4);
223             vertices[quad].vColor = color;
224             vertices[quad+1].vColor = color;
225             vertices[quad+2].vColor = color;
226             vertices[quad+3].vColor = color;
227         }
228    }
229    public ref HipSpriteVertex[4*9] getVertices(){return vertices;}
230 
231 
232     /**
233     *   Use this function instead of build for less overhead
234     */
235     protected void updatePosition()
236     {
237         uint spWidth = sprites[TOP_LEFT].width;
238         uint spHeight = sprites[TOP_LEFT].height;
239         sprites[TOP_LEFT].setPosition(x, y);
240         sprites[TOP_RIGHT].setPosition(x + (width-spWidth), y);
241         sprites[BOT_LEFT].setPosition(x, y + (height-spHeight));
242         sprites[BOT_RIGHT].setPosition(x + (width-spWidth), y + (height-spHeight));
243         sprites[TOP_MID].setPosition(x+spWidth, y);
244         sprites[MID_LEFT].setPosition(x, y+spHeight);
245         sprites[MID_RIGHT].setPosition(x+width-spWidth, y+spHeight);
246         sprites[BOT_MID].setPosition(x+spWidth, y + (height-spHeight));
247         sprites[MID_MID].setPosition(spWidth+x, spHeight+y);
248 
249         for(uint i = 0; i < 9; i++)
250         {
251             uint quad = i*4;
252             HipSpriteVertex[] verts = sprites[i].getVertices();
253             vertices[quad].vPosition = verts[0].vPosition;
254             vertices[quad+1].vPosition = verts[1].vPosition;
255             vertices[quad+2].vPosition = verts[2].vPosition;
256             vertices[quad+3].vPosition = verts[3].vPosition;
257         }
258     }
259 
260 
261 }
262 
263 private enum : ubyte
264 {
265     TOP_LEFT = 0,
266     TOP_MID,
267     TOP_RIGHT,
268 
269     MID_LEFT,
270     MID_MID,
271     MID_RIGHT,
272 
273     BOT_LEFT,
274     BOT_MID,
275     BOT_RIGHT
276 }